#ifndef NATIVEPARSERBASE_H
#define NATIVEPARSERBASE_H

#include <wx/string.h>
#include <map>
#include <queue>
#include "parser/token.h"
#include "parser/tokentree.h"


extern bool s_DebugSmartSense;

class NativeParserBase {
  public:

    enum ParserTokenType {
      pttUndefined = 0,
      pttSearchText,
      pttClass,
      pttNamespace,
      pttFunction
    };


    enum OperatorType {
      otOperatorUndefined = 0,
      otOperatorSquare,
      otOperatorParentheses,
      otOperatorPointer,
      otOperatorStar
    };

    struct ParserComponent {
      wxString        component;
      ParserTokenType tokenType;
      OperatorType    tokenOperatorType;
      ParserComponent() { Clear(); }
      void Clear() {
        component         = wxEmptyString;
        tokenType         = pttUndefined;
        tokenOperatorType = otOperatorUndefined;
      }
    };


    NativeParserBase();


    virtual ~NativeParserBase();

  protected:


    void Reset();

    size_t FindAIMatches( TokenTree*                  tree,
                          std::queue<ParserComponent> components,
                          TokenIdxSet&                result,
                          int                         parentTokenIdx = -1,
                          bool                        isPrefix = false,
                          bool                        caseSensitive = false,
                          bool                        use_inheritance = true,
                          short int                   kindMask = 0xFFFF,
                          TokenIdxSet*                search_scope = 0 );


    void FindCurrentFunctionScope( TokenTree*         tree,
                                   const TokenIdxSet& procResult,
                                   TokenIdxSet&       scopeResult );


    void CleanupSearchScope( TokenTree*  tree,
                             TokenIdxSet* searchScope );


    void GetCallTipHighlight( const wxString& calltip,
                              int*            start,
                              int*            end,
                              int             typedCommas );


    int FindFunctionOpenParenthesis( const wxString& calltip );


    wxString GetCCToken( wxString&        line,
                         ParserTokenType& tokenType,
                         OperatorType&    tokenOperatorType );

    unsigned int FindCCTokenStart( const wxString& line );

    wxString GetNextCCToken( const wxString& line,
                             unsigned int&   startAt,
                             OperatorType&   tokenOperatorType );

    void RemoveLastFunctionChildren( TokenTree* tree, int& lastFuncTokenIdx );
    size_t BreakUpComponents( const wxString&              actual,
                              std::queue<ParserComponent>& components );

    size_t ResolveExpression( TokenTree*                  tree,
                              std::queue<ParserComponent> components,
                              const TokenIdxSet&          searchScope,
                              TokenIdxSet&                result,
                              bool                        caseSense = true,
                              bool                        isPrefix = false );


    void ResolveOperator( TokenTree*          tree,
                          const OperatorType& tokenOperatorType,
                          const TokenIdxSet&  tokens,
                          const TokenIdxSet&  searchScope,
                          TokenIdxSet&        result );


    size_t ResolveActualType( TokenTree*         tree,
                              wxString           searchText,
                              const TokenIdxSet& searchScope,
                              TokenIdxSet&       result );


    void ResolveTemplateMap( TokenTree*         tree,
                             const wxString&    searchStr,
                             const TokenIdxSet& actualTypeScope,
                             TokenIdxSet&       initialScope );


    void AddTemplateAlias( TokenTree*         tree,
                           const int&         id,
                           const TokenIdxSet& actualTypeScope,
                           TokenIdxSet&       initialScope );


    size_t GenerateResultSet( TokenTree*      tree,
                              const wxString& target,
                              int             parentIdx,
                              TokenIdxSet&    result,
                              bool            caseSens = true,
                              bool            isPrefix = false,
                              short int       kindMask = 0xFFFF );


    size_t GenerateResultSet( TokenTree*         tree,
                              const wxString&    target,
                              const TokenIdxSet& ptrParentID,
                              TokenIdxSet&       result,
                              bool               caseSens = true,
                              bool               isPrefix = false,
                              short int          kindMask = 0xFFFF );


    bool IsAllocator( TokenTree*   tree,
                      const int&     id );


    bool DependsOnAllocator( TokenTree*    tree,
                             const int&    id );


    void CollectSearchScopes( const TokenIdxSet& searchScope,
                              TokenIdxSet&       actualTypeScope,
                              TokenTree*         tree );


    int GetTokenFromCurrentLine( TokenTree*         tree,
                                 const TokenIdxSet& tokens,
                                 size_t             curLine,
                                 const wxString&    file );


    void ComputeCallTip( TokenTree*         tree,
                         const TokenIdxSet& tokens,
                         wxArrayString&     items );


    bool PrettyPrintToken( TokenTree*   tree,
                           const Token* token,
                           wxString&    result,
                           bool         isRoot = true );



    static int CountCommas( const wxString& lineText, int start ) {
      int commas = 0;
      int nest = 0;
      while( true ) {
        wxChar c = lineText.GetChar( start++ );
        if( c == '\0' ) {
          break;
        } else if( c == '(' ) {
          ++nest;
        } else if( c == ')' ) {
          --nest;
        } else if( c == ',' && nest == 1 ) {
          ++commas;
        }
      }
      return commas;
    }


    static bool InsideToken( int startAt, const wxString& line ) {
      return ( ( startAt >= 0 )
               && ( ( size_t )startAt < line.Len() )
               && ( ( wxIsalnum( line.GetChar( startAt ) ) )
                    || ( line.GetChar( startAt ) == '_' ) ) );
    }


    static int BeginOfToken( int startAt, const wxString& line ) {
      while( ( startAt >= 0 )
             && ( ( size_t )startAt < line.Len() )
             && ( ( wxIsalnum( line.GetChar( startAt ) ) )
                  || ( line.GetChar( startAt ) == '_' ) ) ) {
        --startAt;
      }
      return startAt;
    }
    static int BeforeToken( int startAt, const wxString& line ) {
      if( ( startAt > 0 )
          && ( ( size_t )startAt < line.Len() + 1 )
          && ( ( wxIsalnum( line.GetChar( startAt - 1 ) ) )
               || ( line.GetChar( startAt - 1 ) == '_' ) ) ) {
        --startAt;
      }
      return startAt;
    }


    static bool IsOperatorEnd( int startAt, const wxString& line ) {
      return ( ( startAt > 0 )
               && ( ( size_t )startAt < line.Len() )
               && ( ( ( line.GetChar( startAt ) == '>' )
                      && ( line.GetChar( startAt - 1 ) == '-' ) )
                    || ( ( line.GetChar( startAt ) == ':' )
                         && ( line.GetChar( startAt - 1 ) == ':' ) ) ) );
    }
    static bool IsOperatorPointer( int startAt, const wxString& line ) {
      return ( ( startAt > 0 )
               && ( ( size_t )startAt < line.Len() )
               && ( ( ( line.GetChar( startAt ) == '>' )
                      && ( line.GetChar( startAt - 1 ) == '-' ) ) ) );
    }



    static bool IsOperatorBegin( int startAt, const wxString& line ) {
      return ( ( startAt >= 0 )
               && ( ( size_t )startAt < line.Len() )
               && ( ( ( line.GetChar( startAt ) == '-' )
                      && ( line.GetChar( startAt + 1 ) == '>' ) )
                    || ( ( line.GetChar( startAt ) == ':' )
                         && ( line.GetChar( startAt + 1 ) == ':' ) ) ) );
    }


    static bool IsOperatorDot( int startAt, const wxString& line ) {
      return ( ( startAt >= 0 )
               && ( ( size_t )startAt < line.Len() )
               && ( line.GetChar( startAt ) == '.' ) );
    }


    static int BeforeWhitespace( int startAt, const wxString& line ) {
      while( ( startAt >= 0 )
             && ( ( size_t )startAt < line.Len() )
             && ( ( line.GetChar( startAt ) == ' ' )
                  || ( line.GetChar( startAt ) == '\t' ) ) ) {
        --startAt;
      }
      return startAt;
    }


    static int AfterWhitespace( int startAt, const wxString& line ) {
      if( startAt < 0 ) {
        startAt = 0;
      }
      while( ( ( size_t )startAt < line.Len() )
             && ( ( line.GetChar( startAt ) == ' ' )
                  || ( line.GetChar( startAt ) == '\t' ) ) ) {
        ++startAt;
      }
      return startAt;
    }
    static bool IsOpeningBracket( int startAt, const wxString& line ) {
      return ( ( ( size_t )startAt < line.Len() )
               && ( ( line.GetChar( startAt ) == '(' )
                    || ( line.GetChar( startAt ) == '[' ) ) );
    }


    static bool IsClosingBracket( int startAt, const wxString& line ) {
      return ( ( startAt >= 0 )
               && ( ( line.GetChar( startAt ) == ')' )
                    || ( line.GetChar( startAt ) == ']' ) ) );
    }

  protected:

  private:




    bool AddChildrenOfUnnamed( TokenTree* tree, const Token* parent, TokenIdxSet& result ) {
      if( ( ( parent->m_TokenKind & ( tkClass | tkEnum ) ) != 0 )
          && parent->m_IsAnonymous == true ) {
        for( TokenIdxSet::const_iterator it = parent->m_Children.begin();
             it != parent->m_Children.end(); ++it ) {
          Token* tokenChild = tree->at( *it );
          if( tokenChild
              && ( parent->m_TokenKind == tkClass || tokenChild->m_Scope != tsPrivate ) ) {
            if( !AddChildrenOfUnnamed( tree, tokenChild, result ) ) {
              result.insert( *it );
              AddChildrenOfEnum( tree, tokenChild, result );
            }
          }
        }
        return true;
      }
      return false;
    }

    bool AddChildrenOfEnum( TokenTree* tree, const Token* parent, TokenIdxSet& result ) {
      if( parent->m_TokenKind == tkEnum ) {
        for( TokenIdxSet::const_iterator it = parent->m_Children.begin();
             it != parent->m_Children.end(); ++it ) {
          Token* tokenChild = tree->at( *it );
          if( tokenChild && tokenChild->m_Scope != tsPrivate ) {
            result.insert( *it );
          }
        }
        return true;
      }
      return false;
    }


    bool IsChildOfUnnamedOrEnum( TokenTree* tree, const int targetIdx, const int parentIdx ) {
      if( targetIdx == parentIdx ) {
        return true;
      }
      if( parentIdx == -1 ) {
        return false;
      }
      Token* parent = tree->at( parentIdx );
      if( parent && ( parent->m_TokenKind & tkClass ) ) {
        for( TokenIdxSet::const_iterator it = parent->m_Children.begin();
             it != parent->m_Children.end(); ++it ) {
          Token* token = tree->at( *it );
          if( token && ( ( ( token->m_TokenKind & tkClass )
                           && ( token->m_IsAnonymous == true ) )
                         || ( token->m_TokenKind & tkEnum ) ) ) {
            if( ( targetIdx == ( *it ) ) || IsChildOfUnnamedOrEnum( tree, targetIdx, ( *it ) ) )
            { return true; }
          }
        }
      }
      return false;
    }



    void AddConstructors( TokenTree *tree, const TokenIdxSet& source, TokenIdxSet& dest );


    bool MatchText( const wxString& text, const wxString& target, bool caseSens, bool isPrefix ) {
      if( isPrefix && target.IsEmpty() ) {
        return true;
      }
      if( !isPrefix ) {
        return text.CompareTo( target, caseSens ? wxString::exact : wxString::ignoreCase ) == 0;
      }
      if( caseSens ) {
        return text.StartsWith( target );
      }
      return text.Upper().StartsWith( target.Upper() );
    }


    bool MatchType( TokenKind kind, short int kindMask ) {
      return kind & kindMask;
    }

  private:
    ParserComponent              m_LastComponent;
    std::map<wxString, wxString> m_TemplateMap;
};

#endif
